﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using FluentMigrator.Infrastructure;
using FluentMigrator.Runner;
using log4net;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Comteck.MigratorCenter {
  /// <summary>
  /// 本地数据镜像帮助类
  /// </summary>
  public static class MigratorHelper {
    /// <summary>
    /// 
    /// </summary>
    private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

    /// <summary>
    /// 更新数据库到最新版本
    /// </summary>
    /// <param name="connStr"></param>
    /// <param name="assemblies"></param>
    public static void UpdateDatabase(string connStr, params Assembly[] assemblies) {
      log.Debug($"当前连接字符串：{connStr}");

      // create services
      var serviceProvider = CreateServices(connStr, assemblies);

      // Put the database update into a scope to ensure
      // that all resources will be disposed.
      using (var scope = serviceProvider.CreateScope()) {
        // 版本信息(数据库当前的版本信息)
        var loader = scope.ServiceProvider.GetRequiredService<IVersionLoader>();

        // 丁建峰增加了一个错误位数的版本号，导致客户端无法更新
        // 已经修复，故删除本行代码 by weijie.sun 2019/6/28
        //loader.DeleteVersion(2019003011046);

        log.Debug($"本地数据库版本号 当前：{loader.VersionInfo.Latest()}");

        // 已加载程序集的所有版本信息
        var migrationInfoList = LoadMigrationsSafe(scope.ServiceProvider);

        // 获取最大版本号
        var maxVersion = migrationInfoList == null || migrationInfoList.Count == 0 ? 0 : migrationInfoList.Last().Key;

        log.Debug($"本地数据库版本号 更新到：{maxVersion}");

        // 只有当前版本号大于0的情况，才允许执行更新脚本
        if (maxVersion > 0) {
          // Instantiate the runner
          var runner = scope.ServiceProvider.GetRequiredService<IMigrationRunner>();
          // 版本管理
          var versionLoader = scope.ServiceProvider.GetRequiredService<IVersionLoader>();

          log.Debug($"数据库脚本 更新开始");

          // 一次性更新到最新版本(但是不适用于新客户第一次发版)
          //runner.MigrateUp();

          // 这里采用循环更新版本的方式
          foreach (var key in migrationInfoList.Keys) {
            try {
              runner.MigrateUp(key);
            } catch {
              versionLoader.UpdateVersionInfo(key, migrationInfoList[key].Description);
            }
          }

          //// 如果还没更新到最新版本，则强制更新到最新版本
          //if (versionLoader.VersionInfo.HasAppliedMigration(maxVersion) == false) {
          //  versionLoader.UpdateVersionInfo(maxVersion, migrationInfoList.Last().Value.Description);
          //}

          log.Debug($"数据库脚本 更新成功");

          // 更新脚本
          //runner.RollbackToVersion(2);
        }
      }
    }

    /// <summary>
    /// 创建IServiceProvider
    /// </summary>
    /// <param name="connStr">数据库连接字符串，如 Data Source=test.db</param>
    /// <param name="assemblies">指定搜索的程序集</param>
    /// <returns></returns>
    private static IServiceProvider CreateServices(string connStr, params Assembly[] assemblies) {
      return new ServiceCollection()
          // Add common FluentMigrator services
          .AddFluentMigratorCore()
          .ConfigureRunner(rb => rb
              // Add SQLite support to FluentMigrator
              .AddSQLite()
              // Set the connection string
              .WithGlobalConnectionString(connStr)
              // Define the assembly containing the migrations
              .ScanIn(assemblies).For.Migrations()
              )
          // Enable logging to console in the FluentMigrator way
          .AddLogging(logger => {
            logger.AddLog4Net("log4net.config", false);
            logger.AddFluentMigratorConsole();
#if DEBUG
            logger.SetMinimumLevel(LogLevel.Debug);
#else
             logger.SetMinimumLevel(LogLevel.Information);
#endif
          })
          // Build the service provider
          .BuildServiceProvider(false);
    }

    /// <summary>
    /// 加载所有的版本信息
    /// </summary>
    /// <param name="serviceProvider">IServiceProvider</param>
    /// <returns></returns>
    private static SortedList<long, IMigrationInfo> LoadMigrationsSafe(IServiceProvider serviceProvider) {
      // IMigrationInformationLoader
      var migrationLoader = serviceProvider.GetRequiredService<IMigrationInformationLoader>();

      var result = new SortedList<long, IMigrationInfo>();

      try {
        result = migrationLoader.LoadMigrations();
      } catch {
        // MissingMigrationsException
      }

      return result;
    }

  }
}
